/*
 * FrequencyOrderedList.java
 *
 * Created on 05 January 2005, 21:18
 */

package org.datamining.guha.model;

import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import org.datamining.guha.model.literal.*;

/**
 * Represents list of all Literals in the model
 * ordered by their frequencies.
 *
 * @author Lukas Vlcek
 */
public class FrequencyOrderedList {

    class LiteralListItem implements Comparable<LiteralListItem> {
    	
        private Literal literal;
        private long frequency;
        
        public LiteralListItem(Literal lit) {
            this.literal = lit;
            this.frequency = 1;
        }
        
        public LiteralListItem(Literal lit, long frequency) {
            this.literal = lit;
            this.frequency = frequency;
        }
        
        public long getFrequency() {
           return this.frequency;
        }

        public Literal getLiteral() {
           return this.literal;
        }

        /** increase frequency by 1 */
        public void increaseFrequency() {
           this.frequency += 1;
        }

        public void increaseFrequency(long value) {
           this.frequency += value;
        }
        
        public int compareTo(LiteralListItem item) {
            
            if (this.frequency != item.frequency) {
                if (this.frequency < item.frequency) {
                    return 1;
                }

                if (this.frequency > item.frequency) {
                    return -1;
                }
            }
            
            if (!this.literal.equals(item.literal)) {
                return this.literal.compareTo(item.literal);
            }

            return 0;   
        }
    }
    
    // List contains all Literals and their frequencies.
    private List<LiteralListItem> literalList;
    
    /** return true if literalList contains item with given Literal */
    private boolean literalListContains(Literal lit) {
        for (int i = literalList.size(); i > 0; i--) {
            LiteralListItem item = literalList.get(i-1);
            if (item.literal.equals(lit)) { return true; }
        }
        return false;
    }
    
    /** Creates a new instance of FrequencyOrderedArrayList */
    public FrequencyOrderedList(int initSize) {
        literalList = new ArrayList<LiteralListItem>(initSize);
    }
    
    /** private constructor
     * Never make this constructor public
     * as it uses inner class as an argument !!!
     */
    private FrequencyOrderedList (List<LiteralListItem> newList) {
        literalList = newList;
    }
    
    /**
     * Update (or/and add) literalList according to given data.
     * @param row LiteralSourceRow.
     */
    public void update(LiteralSourceRow row) {
        List<Literal> literals = row.getSourceLiterals();
        if (literalList.isEmpty()) {
            //LiteralFactory literalFactory = LiteralFactory.getInstance();
            for (int i = literals.size(); i > 0; i--) {
                Literal lit = literals.get(i-1);
                addNewLiteralToList(lit, 1);
            }
        } else {
            for (int i = literals.size(); i > 0; i--) {
                Literal lit = literals.get(i-1);
                if (literalListContains(lit)) {
                    incrementLiteral(lit);
                } else {
                    addNewLiteralToList(lit, 1);
                }
            }
        }
    }
    
    /** Creates new LiteralLiteItem from given arguments
     * and adds it into LiteralList.
     *
     * TODO Chack if given Literal is unique in whole LiteralList.
     * Fire exception if not.
     */
    private void addNewLiteralToList(Literal newLiteral, long frequency) {
        LiteralListItem item = new LiteralListItem(newLiteral,frequency);
        literalList.add(item);
    }
    
    private void incrementLiteral(Literal lit) {
        
        for (int i = literalList.size(); i > 0; i--) {
            LiteralListItem item = literalList.get(i-1);
            if (item.literal.equals(lit)) {
                item.increaseFrequency();
                return;
            }
        }
    }
 
    /**
     * Everytime this method is called collection is sorted.
     * I don't want anybody to modify literalList outside of this class
     * thus new collection is returned.
     * @return List of Literals.
     */
	public List<Literal> getLiteralList() {
		
        Collections.<LiteralListItem>sort(literalList);
        
        // @todo Creating of new List!!!
        List<Literal> result = new ArrayList<Literal>();
        
        int size = literalList.size();
        for (int i = size; i > 0 ; i--) {
            // It is important to return new List in CORRECT order !!!
            LiteralListItem item = literalList.get(size - i);
            result.add((Literal)item.literal);
        }
        
        return result;
    }
    
    /**
     * @return new FrequencyOrderedList which is short of
     * the first (most frequent) Literal.
     */
	public FrequencyOrderedList getChildFrequencyList() {
        Collections.<LiteralListItem>sort(literalList);
        ArrayList<LiteralListItem> newList = new ArrayList<LiteralListItem>(this.literalList);
        if (newList.size() > 0) {
            newList.remove(0);
        }
        return new FrequencyOrderedList(newList);
    }
    
    /** @return long support of given Literal.
     * This method should be friendly. It is called through model.
     */
    public long getLiteralSupport(Literal literal) {
        long support = 0;
        for (int i = literalList.size(); i > 0 ; i--) {        	
            LiteralListItem item = literalList.get(i-1);
            if (item.literal.equals(literal)) {
                return item.frequency;
            }
        }
        return support;
    }
    
	public String toString() {
        Collections.<LiteralListItem>sort(literalList);
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < literalList.size(); i++) {
            LiteralListItem item = literalList.get(i);
            sb.append(item.literal + "[" + item.frequency + "] ");
        }
        return sb.toString();
    }
}
